PIC Tutorial Seven - RS232
       
       For these tutorials you require the Main Board, Main Board 2, LCD 
      Board, Serial Board, LED Board and switch board. Download 
      zipped tutorial files, a number of examples for the 16F876 based Main 
      Board 2 are provided, these have an 'a' at the end of the filename - the 
      rest are left for the user to convert as an exercise. 
       RS232 is an asynchronous serial communications protocol, widely 
      used on computers. Asynchronous means it doesn't have any separate 
      synchronising clock signal,  so it has to synchronise itself to the 
      incoming data - it does this by the use of 'START' and 'STOP' pulses. The 
      signal itself is slightly unusual for computers, as rather than the 
      normal 0V to 5V range, it uses +12V to -12V - this is done to improve 
      reliability, and greatly increases the available range it can work over - 
      it isn't necessary to provide this exact voltage swing, and you can 
      actually use the PIC's 0V to 5V voltage swing with a couple of resistors 
      to make a simple RS232 interface which will usually work well, but isn't 
      guaranteed to work with all serial ports. For this reason I've designed 
      the Serial Board to use the MAX232 chip, this is a chip specially designed 
      to interface between 5V logic levels and the +12V/-12V of RS232 - it 
      generates the +12V/-12V internally using capacitor charge pumps, and 
      includes four converters, two transmit and two receive, the Serial Board 
      only makes use of one of each - the other two are clearly marked on the 
      circuit, and can be used for something else if required. 
       There are various data types and speeds used for RS232, I'm going 
      to concentrate on the most common type in use, known as 8N1 - the 8 
      signifies '8 Data Bits', the N signifies 'No Parity' (can also be E 'Even 
      Parity' or O 'Odd Parity'), the final 1 signifies '1 Stop Bit'. The total 
      data sent consists of  1 start bit, 8 data bits, and 1 stop bit - 
      giving a total of 10 bits. For the speed, I'm going to concentrate on 
      9600BPS (Bits Per Second), as each byte sent has 10 bits this means we can 
      transfer a maximum of 960 bytes of data per second - this is fairly fast, 
      but pretty easy to do in software, it's easily modified if you need faster 
      or slower speeds, all  you need to do is alter the delay timings - 
      but I find 9600BPS is a pretty good speed to use. 
       We now know that we will be sending or receiving 960 ten bit data 
      bytes per second, from that it's simple to calculate how long each bit is 
      - simply divide 1 second by 9600 - this gives 104uS per bit. This value is 
      crucial to successful RS232 communication, it doesn't have to be exact as 
      the stop pulse allows resynchronisation after each data byte, but it must 
      be accurate enough to maintain reading in the correct bit throughout each 
      byte. The data is sent low bit first, so the example in the diagrams below 
      is sending '01001011 Binary', '4B Hex', '75 Decimal'. 
       OK,  now we know all the details of the protocol we are 
      using,  I'll explain how we transmit a byte: 
      
        - The RS232 signal needs to be in the 'STOP CONDITION', at -12V, as 
        the MAX232 inverts (a '1' is -12V and a '0' +12V) we need to make sure 
        the PIC output pin is set HIGH, this should be done in the 
        initialisation section of the program - this pin should always be high, 
        EXCEPT when we are sending data, when it can be either high or 
        low. 
        
 - The RS232 line is now happily sat at -12V, and the receiving device 
        is waiting for a 'START BIT', to generate this all we need to do is set 
        the PIC output pin low, the MAX232 inverts the signal and takes the 
        RS232 line up to +12V. As we know that all bits should be 104uS long we 
        now delay 104uS, before we do anything else. 
        
 - Now we can transmit the 8 data bytes, starting with the low bit, 
        after each bit is set on the output pin we again wait 104uS, so each bit 
        is the correct length. 
        
 - That only leaves the 'STOP BIT', for this we set the PIC output pin 
        HIGH (as in section 1 above), and wait 104uS to give the correct bit 
        length - now the 'STOP BIT'  doesn't have to be only 104uS long, it 
        simply signifies the end of the data byte. If it is the last data byte 
        it could be a considerable time before another 'START BIT' is sent - 
        this is shown in the diagrams by the large gap between the end of the 
        'STOP BIT' (shown by the dotted line) and the next 'START BIT'. If you 
        are sending data as fast as possible the next 'START BIT' will start on 
        that dotted line, immediately after the 104uS 'STOP BIT'. 
  
      
      
      
        
        
            | 
           This is an example of a signal on an RS232 line, initially 
            it sits at -12V, known as the 'STOP CONDITION', this condition lasts 
            until a signal is sent. To send a signal we first need to let the 
            receiving device know we are starting to send data, to do this we 
            set the line to +12V, this is called the 'START BIT' - the receiving 
            device is waiting for this to happen, once it does it then gets 
            ready to read the next 9 bits of data (eight data bits and one stop 
            bit). |    
      
      
      
        
        
           This is the identical signal as it leaves (or enters) the 
            PIC pin, as the MAX232 inverts the signal this looks to be inverted, 
            but is actually the correct way up - RS232 logic levels are inverted 
            compared to normal levels. | 
            |    
       To receive a data byte is pretty straightforward as well: 
      
        - Test the PIC input pin, and loop until it goes low, signifying the 
        beginning of the 'START BIT'. 
        
 - Now we wait just half a bit time (52uS) and check again to make sure 
        it's still low - this 52uS delay means we are reading the 'START BIT' 
        pretty well in the centre of the pulse, where it should be the most 
        reliable. 
        
 - Following a successful 'START BIT' we can now read the data bits, as 
        we are currently in the centre of the 'START BIT' we can simply wait 
        104uS, which will take us to the centre of the first data bit, then read 
        the input pin again, remembering to invert the polarity of the bit. We 
        then read the next seven bits in the same way, waiting 104uS before each 
        one. 
        
 - Lastly we need to account for the 'STOP BIT', again we wait 104uS 
        for the centre of the bit and could read the port pin if we wanted, if 
        it isn't high there has obviously been an error, but  for 
        simplicity we just exit the routine. 
        
 - We now can transfer the received byte to where we wish, and either 
        wait for another byte or do something else. 
  
       Here are the actual serial routines we will be using, they 
      consist of a number of small subroutines, and require four data registers 
      allocating: 
      
        - Xmit_Byte - this is used to store the transmitted byte (passed in 
        W). 
        
 - Rcv_Byte - this is used for the received byte, and is copied to W on 
        exiting the routine. 
        
 - Bit_Cntr - used to count the number of bits sent or received, set to 
        8 and decremented. 
        
 - Delay_Count - used in the two delay routines. 
  
       The routines themselves consist of three subroutines that are 
      called, and two internal subroutines, not normally called from 
      elsewhere: 
      
        - SER_INIT - this is only ever called once, usually when the 
        program first runs, as part of the normal initialisation stages, it sets 
        the input and output pins to the correct direction, and sets the output 
        pin to the correct polarity - high, so the RS232 line sets at -12V. 
        
 - XMIT_RS232 - this is the transmit routine, simply load the 
        byte to be transmitted into the W register and call this subroutine 
        (CALL  XMIT_RS232). 
        
 - Rcv_RS232 - this is the receive routine, when you call this 
        is waits for a received byte, there's no timeout, so it will wait for 
        ever if it doesn't receive a byte. To use the subroutine simply call it 
        (CALL  Rcv_RS232) and it returns the received byte in the W 
        register. 
        
 - Start_Delay - internal subroutine that delays 52uS, used by 
        the Rcv_RS232 subroutine to delay half a bit length. 
        
 - Bit_Delay - used by both the transmit and receive 
        subroutines, to provide a 104uS (one bit) delay. 
  ;Serial routines
            Xmit_Byte    Equ  0x20        ;holds byte to xmit
            Rcv_Byte     Equ  0x21        ;holds received byte 
            Bit_Cntr     Equ  0x22        ;bit counter for RS232
            Delay_Count  Equ  0x23        ;delay loop counter
SER_INIT
            BSF     STATUS, RP0           ;select bank 1
            BCF     TRISB, 6              ;set B6 as an output
            BSF     TRISB, 7              ;set B7 as an input
            BCF     STATUS, RP0           ;select bank 0
            BSF     PORTB, 6              ;set B6 high
            RETURN
XMIT_RS232  MOVWF   Xmit_Byte             ;move W to Xmit_Byte
            MOVLW   0x08                  ;set 8 bits out
            MOVWF   Bit_Cntr
            BCF     PORTB, 6
            CALL    Bit_Delay
Ser_Loop    RRF     Xmit_Byte , f         ;send one bit
            BTFSS   STATUS    , C
            BCF     PORTB, 6
            BTFSC   STATUS    , C
            BSF     PORTB, 6
            CALL    Bit_Delay
            DECFSZ  Bit_Cntr  , f         ;test if all done
            GOTO    Ser_Loop
            BSF     PORTB, 6
            CALL    Bit_Delay
            RETURN
Rcv_RS232   BTFSC   PORTB, 7              ;wait for start bit
            GOTO    Rcv_RS232
            CALL    Start_Delay	          ;do half bit time delay
            BTFSC   PORTB, 7              ;check still in start bit
            GOTO    Rcv_RS232
            MOVLW   0x08                  ;set up to read 8 bits
            MOVWF   Bit_Cntr
            CLRF    Rcv_Byte
Next_RcvBit CALL    Bit_Delay
            BTFSS   PORTB, 7
            BCF     STATUS    , C
            BTFSC   PORTB, 7
            BSF     STATUS    , C
            RRF     Rcv_Byte  , f
            DECFSZ  Bit_Cntr  , f         ;test if all done
            GOTO    Next_RcvBit
            CALL    Bit_Delay
            MOVF    Rcv_Byte, W
            RETURN
Start_Delay MOVLW   0x0C
            MOVWF   Delay_Count
Start_Wait  NOP
            DECFSZ  Delay_Count , f
            GOTO    Start_Wait
            RETURN
Bit_Delay   MOVLW   0x18
            MOVWF   Delay_Count
Bit_Wait    NOP
            DECFSZ  Delay_Count , f
            GOTO    Bit_Wait
            RETURN
        
       The  routines presented here use PortB pin 6 as the output, 
      and PortB pin 7 as the input, they are based on a 4MHz clock frequency. As 
      it's all done in software you can easily change the port and pin 
      designations, and simply alter the delay timings for different clock 
      speeds or baud rates. 
      Tutorial 7.1 - required hardware, Main Board and Serial 
      Board. 
       This first sample program simply transmits a few ASCII characters 
      out of the serial board, it displays 'RS232'. In this example each 
      character is individually loaded in to the W register and the XMIT_RS232 
      subroutine is called. 
      Tutorial 7.2 - required hardware, Main Board and Serial 
      Board. 
       This second sample program transmits two lines of text, the text 
      is stored as a string (terminated by '0x00') in the top page of memory, 
      the two characters '0x0A' and '0x0D' in the string are LF and CR to move 
      the cursor to the start of the next line. The XMIT_RS232 subroutine is 
      called repeatedly in the loop which reads the string. 
      Tutorial 7.3 - required hardware, Main Board, LCD Board 
      and Serial Board. 
       This third sample program receives data one character at a time 
      and displays it on the LCD module. Please note that both this, and the 
      next tutorial, can only handle one character at a time - as there's no 
      handshaking involved the routine on the PIC must finish whatever it has to 
      before the next character arrives - if a continuous stream of data is 
      incoming it only has 52uS before the next byte arrives, and this is too 
      fast for the LCD to have finished displaying the previous character. There 
      are various ways of overcoming this - firstly, as long  as you are 
      typing the characters on the keyboard there won't be a problem (you can't 
      type fast enough), secondly you could arrange for the transmitted protocol 
      to have more than one stop bit (two stop bits would  give three times 
      as long to display the characters, three stop bits would give five times 
      as long, and so on). Or you could buffer the characters in PIC data 
      registers, this still wouldn't allow a continuous data stream, but would 
      probably do all that's required. (For a further possibility see Tutorial 
      7.7a) 
      Tutorial 7.4 - required hardware, Main Board, LCD Board 
      and Serial Board. 
       This fourth sample program receives data one character at a time, 
      displays it on the LCD module (as in 7.3) and then echo's the character 
      back to the PC screen. 
      Tutorial 7.5 - required hardware, Main Board, LED Board 
      and Serial Board. 
       This fifth sample program receives data one character at a time, 
      displays it on the LED board and then echo's the character back to the PC 
      screen, the ports have been swapped around (PortA now connects to the 
      serial board, and PortB connects to the LED board, because pin A4 is an 
      open-collector output). This would make a nice simple way of providing 
      eight switched outputs controlled from a serial port. 
      Tutorial 7.6 - required hardware, Main Board, Switch 
      Board and Serial Board. 
       This sixth sample program receives one data byte from the PC (any 
      byte - just to initiate a read), reads the switches connected to PortB, 
      and sends the result back to the PC as a string of eight 1's and 0's - B0 
      first, and B7 last - followed by CRLF. If you examine the code, the 
      routine for sending the PortB reading as a series of ASCII 1's and 0's is 
      based on the XMIT_RS232 code, it reads the bits in turn in exactly the 
      same way, only the action taken for each bit is different. As the previous 
      example makes a nice easy way of writing eight bits, this one makes a nice 
      easy way of reading eight bits. 
      Tutorial 7.7a - required hardware, Main Board 2, LCD 
      Board and Serial Board. 
       This seventh sample program works exactly like Tutorial 7.4, but 
      is based on the 16F876 at 20MHz, and uses the hardware USART rather than 
      software emulation. As it uses hardware to receive the serial data, this 
      gives a lot more time for processing and displaying characters, around 1mS 
      or so. There isn't a 16F628 version of this tutorial yet as I have to 
      change the serial board connections over, as soon as this is done I'll 
      post a 16F628 version as well - if you want to do it, the values for the 
      USART are SPBRG=25 and BRGH=1. 
       
     |